<?php
/**
 * BootAdmin is committed to providing solutions for quickly building web applications
 * Please view the LICENSE file that was distributed with this source code,
 * For the full copyright and license information.
 * Thank you very much for using BootAdmin.
 *
 * @Author G.r<root@BootAdmin.com>
 */

declare (strict_types=1);

namespace app\common\model;

use app\admin\service\Auth;
use think\db\BaseQuery as Query;
use think\db\exception\DbException;
use think\Exception;
use think\helper\Str;
use think\Model;
use think\Paginator;

/**
 * 模型基类
 */
class BaseModel extends Model
{
    // 定义全局的查询范围
    protected $globalScope = ['scope'];

    /**
     * 当前数据
     * @var mixed
     */
    protected $params = [];

    /**
     * 查询条件
     * @var Query
     */
    protected $query;

    /**
     * 允许修改的字段
     * @var array
     */
    protected array $allowModifyFields = [];

    /**
     * 列表字段
     * @var array
     */
    protected array $fields = [];

    /**
     * 错误信息
     * @var string
     */
    protected string $error;

    /**
     * 实例化
     * @param array $data
     */
    public function __construct(array $data = [])
    {
        parent::__construct($data);
        $this->query = $this->db();
        $this->initialize();
    }

    /**
     * 初始化
     */
    public function initialize(){}

    /**
     * 新增事件回调
     * @param $model
     * @return false|void
     */
    public static function onBeforeInsert($model)
    {
        $query = (new static())->db();
        $table = $query->getTable();

        $field = $query->getConnection()->getTableFields($table);

        if (in_array('create_id', $field)) {
            $model->create_id = request()->adminId;
        }
    }

    /**
     * 获取错误信息
     * @return string
     */
    public function getError() : string
    {
        return $this->error;
    }


    /**
     * 设置错误信息
     * @param $error
     */
    public function setError($error) : void
    {
        $this->error = $error;
    }

    /**
     * 数据边界处理
     * @param $query
     */
    public function scopeScope($query)
    {
        $ids = Auth::getDataScope();
        if (!empty($ids)) {
            $query->whereIn('create_id',$ids);
        }
    }

    /**
     * 设置数据
     * @param mixed $params
     * @return BaseModel
     */
    public function setParams(mixed $params): BaseModel
    {
        $this->params = $params;
        return $this;
    }

    /**
     * 排除入库字段
     * @param $fields
     * @return BaseModel
     */
    public function excludeFields($fields): BaseModel
    {
        foreach ($fields as $field) {
            unset($this->params[$field]);
        }
        return $this;
    }

    /**
     * 获取分页列表
     * @param bool $onlyTrashed
     * @return bool|Paginator
     */
    public function getPage(bool $onlyTrashed = false): bool|Paginator
    {
        $limit = $this->params['limit'] ?? 10;
        try {
            return $this->queryBuilder($onlyTrashed)->paginate($limit);
        } catch (DbException $e) {
            $this->setError($e->getMessage());
            return false;
        }
    }

    /**
     * 获取列表
     */
    public function getList(bool $onlyTrashed = false)
    {
        try {
            return $this->queryBuilder($onlyTrashed)->field($this->fields)->select()->toArray();
        } catch (DbException $e) {
            $this->setError($e->getMessage());
            return false;
        }
    }

    /**
     * 搜索构建器
     */
    public function queryBuilder($onlyTrashed = false)
    {
        $search = [];
        foreach ($this->params as $key => $value) {
            // 检测搜索器
            $method    = 'search' . Str::studly($key) . 'Attr';
            if (method_exists($this, $method)) {
                $search[] = $key;
            }
        }
        if (!empty($search)) {
            $this->query = $this->query->withSearch($search, $this->params);
        }
        if ($onlyTrashed) {
            $this->query = $this->query->onlyTrashed();
        }
        return $this->query;
    }

    /**
     * 新增数据
     * @return bool
     */
    public function add(): bool
    {
        $result = false;
        $this->startTrans();
        try {
            $result = $this->save($this->params);
            $this->commit();
        } catch (Exception $e) {
            $this->rollback();
            $this->setError($e->getMessage());
        }
        return $result;
    }

    /**
     * 编辑数据
     * @param $id
     * @return bool
     */
    public function edit($id): bool
    {
        try {
            $row = $this->find($id);
            if (empty($row)) {
                $this->setError('数据不存在');
                return false;
            }
        } catch (Exception $e) {
            $this->setError($e->getMessage());
            return false;
        }
        $result = false;
        $this->startTrans();
        try {
            $result = $row->save($this->params);
            $this->commit();
        } catch (Exception $e) {
            $this->rollback();
            $this->setError($e->getMessage());
        }
        return $result;
    }

    /**
     * 删除数据
     * @param $ids
     * @return bool
     */
    public function del($ids): bool
    {
        $pk    = $this->getPk();
        try {
            $data = $this->where($pk, 'in', $ids)->select();
        } catch (DbException $e) {
            $this->setError($e->getMessage());
            return false;
        }
        $count = 0;
        $this->startTrans();
        try {
            foreach ($data as $row) {
                $count += $row->delete();
            }
            $this->commit();
        } catch (Exception $e) {
            $this->rollback();
            $this->setError($e->getMessage());
        }
        return $count > 0;
    }

    /**
     * 真实删除数据
     * @param $ids
     * @return bool
     */
    public function realDel($ids): bool
    {
        $pk    = $this->getPk();
        try {
            $data  = $this->where($pk, 'in', $ids)->onlyTrashed()->select();
        } catch (DbException $e) {
            $this->setError($e->getMessage());
            return false;
        }
        $count = 0;
        $this->startTrans();
        try {
            foreach ($data as $v) {
                $count += $v->force()->delete();
            }
            $this->commit();
        } catch (Exception $e) {
            $this->rollback();
            $this->setError($e->getMessage());
        }
        return $count > 0;
    }

    /**
     * 恢复数据
     * @param $ids
     * @return bool
     */
    public function recovery($ids): bool
    {
        $pk    = $this->getPk();
        try {
            $data  = $this->where($pk, 'in', $ids)->onlyTrashed()->select();
        } catch (DbException $e) {
            $this->setError($e->getMessage());
            return false;
        }
        $count = 0;
        $this->startTrans();
        try {
            foreach ($data as $v) {
                $count += $v->restore();
            }
            $this->commit();
        } catch (Exception $e) {
            $this->rollback();
            $this->setError($e->getMessage());
        }
        return $count > 0;
    }

    /**
     * 字段修改
     * @param $id
     * @return bool
     */
    public function modify($id): bool
    {
        if (!in_array($this->params['field'], $this->allowModifyFields)) {
            $this->setError('该字段不允许修改：' . $this->params['field']);
            return false;
        }
        $pk    = $this->getPk();
        try {
            $row  = $this->where($pk, $id)->find();
        } catch (DbException $e) {
            $this->setError($e->getMessage());
            return false;
        }
        $result = false;
        $this->startTrans();
        try {
            $result = $row->save([
                $this->params['field'] => $this->params['value'],
            ]);
            $this->commit();
        } catch (Exception $e) {
            $this->rollback();
            $this->setError($e->getMessage());
        }
        return $result;
    }

    /**
     * 生成一棵树
     * @param array $data
     * @param int $parentId
     * @param string $id
     * @param string $parentField
     * @param string $children
     * @return array
     */
    public function toTree(array $data = [], int $parentId = 0, string $id = 'id', string $parentField = 'parent_id', string $children='children'): array
    {
        if (empty($data)) return [];

        $tree = [];

        foreach ($data as $value) {
            if ($value[$parentField] == $parentId) {
                $child = $this->toTree($data, $value[$id], $id, $parentField, $children);
                if (!empty($child)) {
                    $value[$children] = $child;
                }
                array_push($tree, $value);
            }
        }

        unset($data);
        return $tree;
    }
}